import unittest
import sys
import os
from typing import Optional, List
import importlib.util


class SimpleApiTest:
    """基于unittest的SimpleAPI测试运行器"""
    
    def __init__(self, test_directory: str = "tests", verbosity: int = 2):
        """
        初始化测试运行器
        
        Args:
            test_directory: 测试文件所在目录
            verbosity: 测试输出详细程度 (0=静默, 1=正常, 2=详细)
        """
        self.test_directory = test_directory
        self.verbosity = verbosity
        self.test_suite = unittest.TestSuite()
        self.test_loader = unittest.TestLoader()
        
    def discover_tests(self, pattern: str = "test_*.py") -> None:
        """
        自动发现测试文件
        
        Args:
            pattern: 测试文件名模式
        """
        if not os.path.exists(self.test_directory):
            print(f"警告: 测试目录 '{self.test_directory}' 不存在")
            return
            
        # 手动遍历测试目录，避免unittest.discover的导入问题
        for filename in os.listdir(self.test_directory):
            if filename.startswith("test_") and filename.endswith(".py"):
                test_file_path = os.path.join(self.test_directory, filename)
                self.add_test_file(test_file_path)
    
    def add_test_file(self, test_file_path: str) -> None:
        """
        添加指定的测试文件
        
        Args:
            test_file_path: 测试文件路径
        """
        if not os.path.exists(test_file_path):
            print(f"警告: 测试文件 '{test_file_path}' 不存在")
            return
            
        try:
            # 动态导入测试模块
            module_name = os.path.splitext(os.path.basename(test_file_path))[0]
            spec = importlib.util.spec_from_file_location(module_name, test_file_path)
            if spec and spec.loader:
                module = importlib.util.module_from_spec(spec)
                
                # 添加当前目录到sys.path，确保可以导入simpleapi模块
                current_dir = os.getcwd()
                if current_dir not in sys.path:
                    sys.path.insert(0, current_dir)
                
                sys.modules[module_name] = module
                spec.loader.exec_module(module)
                
                # 加载模块中的测试
                tests = self.test_loader.loadTestsFromModule(module)
                self.test_suite.addTest(tests)
                
        except Exception as e:
            print(f"警告: 无法加载测试文件 '{test_file_path}': {e}")
    
    def add_test_class(self, test_class) -> None:
        """
        添加指定的测试类
        
        Args:
            test_class: 测试类
        """
        tests = self.test_loader.loadTestsFromTestCase(test_class)
        self.test_suite.addTest(tests)
    
    def run(self, exit_on_failure: bool = True) -> unittest.TestResult:
        """
        运行所有测试
        
        Args:
            exit_on_failure: 测试失败时是否退出程序
            
        Returns:
            测试结果对象
        """
        print("=" * 70)
        print("SimpleAPI 测试运行器")
        print("=" * 70)
        
        # 如果没有手动添加测试，则自动发现
        if self.test_suite.countTestCases() == 0:
            print("自动发现测试文件...")
            self.discover_tests()
        
        if self.test_suite.countTestCases() == 0:
            print("没有找到任何测试用例")
            return unittest.TestResult()
        
        print(f"发现 {self.test_suite.countTestCases()} 个测试用例")
        print("-" * 70)
        
        # 创建测试运行器
        runner = unittest.TextTestRunner(
            verbosity=self.verbosity,
            stream=sys.stdout,
            buffer=True  # 捕获测试期间的输出
        )
        
        # 运行测试
        result = runner.run(self.test_suite)
        
        # 打印总结
        print("\n" + "=" * 70)
        print("测试总结:")
        print(f"运行测试: {result.testsRun}")
        print(f"成功: {result.testsRun - len(result.failures) - len(result.errors)}")
        print(f"失败: {len(result.failures)}")
        print(f"错误: {len(result.errors)}")
        print(f"跳过: {len(result.skipped) if hasattr(result, 'skipped') else 0}")
        
        if result.failures:
            print(f"\n失败的测试:")
            for test, traceback in result.failures:
                print(f"  - {test}")
        
        if result.errors:
            print(f"\n错误的测试:")
            for test, traceback in result.errors:
                print(f"  - {test}")
        
        success_rate = ((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100) if result.testsRun > 0 else 0
        print(f"\n成功率: {success_rate:.1f}%")
        print("=" * 70)
        
        # 根据测试结果决定是否退出
        if exit_on_failure and (result.failures or result.errors):
            print("测试失败，程序退出")
            sys.exit(1)
        elif result.testsRun > 0 and not result.failures and not result.errors:
            print("所有测试通过！")
        
        return result
    
    def run_specific_tests(self, test_names: List[str]) -> unittest.TestResult:
        """
        运行指定名称的测试
        
        Args:
            test_names: 测试名称列表
            
        Returns:
            测试结果对象
        """
        suite = unittest.TestSuite()
        
        for test_name in test_names:
            try:
                # 尝试加载指定的测试
                test = self.test_loader.loadTestsFromName(test_name)
                suite.addTest(test)
            except Exception as e:
                print(f"无法加载测试 '{test_name}': {e}")
        
        if suite.countTestCases() == 0:
            print("没有找到指定的测试用例")
            return unittest.TestResult()
        
        runner = unittest.TextTestRunner(verbosity=self.verbosity)
        return runner.run(suite)
    
    def list_tests(self) -> List[str]:
        """
        列出所有可用的测试
        
        Returns:
            测试名称列表
        """
        if self.test_suite.countTestCases() == 0:
            self.discover_tests()
        
        test_names = []
        
        def extract_test_names(suite_or_case):
            if isinstance(suite_or_case, unittest.TestSuite):
                for test in suite_or_case:
                    extract_test_names(test)
            else:
                test_names.append(str(suite_or_case))
        
        extract_test_names(self.test_suite)
        return test_names
    
    def set_verbosity(self, verbosity: int) -> None:
        """
        设置输出详细程度
        
        Args:
            verbosity: 详细程度 (0=静默, 1=正常, 2=详细)
        """
        self.verbosity = max(0, min(2, verbosity))


# 便捷函数
def run_all_tests(test_directory: str = "tests", verbosity: int = 2) -> unittest.TestResult:
    """
    运行所有测试的便捷函数
    
    Args:
        test_directory: 测试目录
        verbosity: 详细程度
        
    Returns:
        测试结果
    """
    test_runner = SimpleApiTest(test_directory, verbosity)
    return test_runner.run()


def run_test_file(test_file: str, verbosity: int = 2) -> unittest.TestResult:
    """
    运行指定测试文件的便捷函数
    
    Args:
        test_file: 测试文件路径
        verbosity: 详细程度
        
    Returns:
        测试结果
    """
    test_runner = SimpleApiTest(verbosity=verbosity)
    test_runner.add_test_file(test_file)
    return test_runner.run() 